home *** CD-ROM | disk | FTP | other *** search
/ MacWorld UK 2000 March / MW_UK_2000_03.iso / Shareware World / Utilities / Text Processing / Alpha / Tcl / Modes / cMode.tcl < prev    next >
Encoding:
Text File  |  1999-10-25  |  22.5 KB  |  715 lines  |  [TEXT/ALFA]

  1. ## -*-Tcl-*- (install) (nowrap)
  2.  # ###################################################################
  3.  #    Vince's    Additions -    an extension package for Alpha
  4.  # 
  5.  #    FILE: "cMode.tcl"
  6.  #                    created: 19/4/96 {4:53:38 pm}    
  7.  #                 last update: 25/10/1999 {11:21:20 pm}    
  8.  #    Author:    Vince Darley
  9.  #    E-mail:    <vince@santafe.edu>
  10.  #      mail:    317 Paseo de Peralta, Santa Fe, NM 87501, USA
  11.  #       www:    <http://www.santafe.edu/~vince/>
  12.  #    
  13.  # ###################################################################
  14.  ##
  15.  
  16. alpha::mode C 1.2.5 dummyC { *.r } \
  17.   {electricReturn electricBraces electricSemicolon}
  18. alpha::mode C++ 1.2.5 dummyC++ \
  19.   {*.H *.c *.h *.cc *.cp *.cpp *.CPP *.C *.pch *.pch++ *.icc *.exp *.c++} \
  20.   {electricReturn electricBraces electricSemicolon}
  21.  
  22. # not really sure this is required any more
  23. # the CW menu registers a proc to call cw IDE.
  24. hook::register saveHook modified "C" "C++"
  25.  
  26. proc dummyC {} {}
  27. proc dummyC++ {} {}
  28.  
  29. newPref f elecColon {1} C
  30. newPref v leftFillColumn {3} C
  31. newPref v prefixString {//} C
  32. newPref f elecElse {1} C
  33. newPref f wordWrap {0} C
  34. newPref v funcExpr {^[^ \t\(#\r/@].*\(.*\)$} C
  35. newPref v parseExpr {\b([_:\w]+)\s*\(} C
  36. newPref v wordBreak {[_\w]+} C
  37. newPref v wordBreakPreface {[^_\w]} C
  38. newPref f autoMark 0 C
  39. newPref v stringColor green C
  40. newPref v commentColor red C
  41. newPref v funcColor yellow C
  42. newPref v keywordColor blue C
  43. newPref v CWCompSig CWIE C
  44. newPref v CWDbgSig MWDB C
  45. newPref v SymCompSig KAHL C
  46. newPref v SymDbgSig {◊LSD} C
  47. newPref f includeMenu {1} C
  48. newPref f launchIDEifRequired {1} C
  49. newPref v sourceSuffices { .c } C
  50. newPref v headerSuffices { .h } C
  51. newPref v indentComments "code 0" C
  52. newPref v indentMacros "fixed 0" C
  53. newPref v IDE 0 C "" [list "CodeWarrior" "Symantec" "none"] index
  54. newPref f useFasterButWorseIndentation 0 C
  55. set C::commentRegexp    {/\*(([^*]/)|[^*]|\r)*\*/}
  56. set cPreRegexp        {^\#[\t ]*[a-z]*}
  57. set cKeyWords {
  58.     void break register short enum extern int for if while struct static 
  59.     long continue switch case char unsigned double float return else 
  60.     default goto do pascal Boolean typedef volatile union auto sizeof 
  61.     size_t
  62. }
  63. if {[info exists Cwords]} {set cKeyWords [concat $cKeyWords $Cwords]}
  64. regModeKeywords -e {//} -b {/*} {*/} -c $CmodeVars(commentColor) \
  65.   -f $CmodeVars(funcColor) -k $CmodeVars(keywordColor) \
  66.   -s $CmodeVars(stringColor) -m {#} C $cKeyWords
  67.  
  68. #================================================================================
  69.  
  70. newPref f elecColon {1} C++
  71. newPref v leftFillColumn {3} C++
  72. newPref v prefixString {//} C++
  73. newPref v wordBreak {[\w_]+} C++
  74. newPref v wordBreakPreface {[^_\w]} C++
  75. newPref f elecElse {1} C++
  76. newPref f wordWrap {0} C++
  77. newPref v funcExpr {^([^ \t\(#[\r\n]/@].*[ \t]+)?\*?([A-Za-z0-9~_]+(<[^>]*>)?::[-A-Za-z0-9~_+= <>\|\*/]+|[A-Za-z0-9~_]+)[ \t\r\n]*\(} C++
  78. newPref v parseExpr {\b([_:\w]+)\s*\(} C++
  79. newPref f autoMark 0 C++
  80. newPref v stringColor green C++
  81. newPref v commentColor red C++
  82. newPref v keywordColor blue C++
  83. newPref v funcColor yellow C++
  84. newPref v CWCompSig CWIE C++
  85. newPref v CWDbgSig MWDB C++
  86. newPref v SymCompSig KAHL C++
  87. newPref v SymDbgSig {◊LSD} C++
  88. newPref f includeMenu {1} C++
  89. newPref f launchIDEifRequired {1} C++
  90. newPref v sourceSuffices { .cc .cp .cpp .c .icc .C } C++
  91. newPref v headerSuffices { .h .hh } C++
  92. # These three are pairs:
  93. newPref v indentComments "code 0" C++ "" indentationTypes varitem
  94. newPref v indentC++Comments "code 0" C++ "" indentationTypes varitem
  95. newPref v indentMacros "fixed 0" C++ "" indentationTypes varitem
  96. newPref v IDE 0 C++ "" [list "CodeWarrior" "Symantec" "none"] index
  97. newPref f useFasterButWorseIndentation 0 C++
  98. newPref folder universalHeadersFolder "" C++
  99.  
  100. proc C++::openUniversalHeader {} {
  101.     global universalHeadersFolder tabSize
  102.     if {![file exists $universalHeadersFolder]} {
  103.     alertnote "Please set your 'Universal Headers Folder' preference first.\
  104.       It's set to '$universalHeadersFolder' which doesn't exist."
  105.     return
  106.     }
  107.     set filename [prompt::statusLineComplete "Open which header" \
  108.       [list file::completeFromDir $universalHeadersFolder] -nocache \
  109.       -tryuppercase]
  110.     set old $tabSize
  111.     set tabSize 4
  112.     file::openQuietly [file join $universalHeadersFolder $filename]
  113.     set tabSize $old
  114. }
  115.  
  116. Bind 'q' <o> C++::openUniversalHeader C++
  117.  
  118. set C++::commentRegexp    {/\*(([^*]/)|[^*]|\r)*\*/}
  119.  
  120. set {c++KeyWords} {
  121.     new delete explicit class friend protected private public template try 
  122.     catch throw operator const mutable virtual asm inline this and and_eq 
  123.     bitand bitor compl not or or_eq xor xor_eq not_eq wchar_t bool true 
  124.     false bool inline mutable static_cast dynamic_cast reinterpret_cast 
  125.     typeid using namespace inherited
  126. }
  127. if {[info exists {C++words}]} {
  128.     set {c++KeyWords} [concat ${c++KeyWords} ${C++words} $cKeyWords]
  129. } else {
  130.     set {c++KeyWords} [concat ${c++KeyWords} $cKeyWords]
  131. }
  132.  
  133. regModeKeywords -e {//} -b {/*} {*/} -c [set C++modeVars(commentColor)] \
  134.   -f [set C++modeVars(funcColor)] -k [set C++modeVars(keywordColor)] \
  135.   -s [set C++modeVars(stringColor)] -m {#} {C++} ${c++KeyWords}
  136. unset cKeyWords
  137. unset {c++KeyWords}
  138.  
  139. proc C++::DblClick {from to shift option control} {    
  140.     if {[regexp {#include.*("|<)(.*)("|>)} [getText \
  141.       [lineStart [getPos]] [nextLineStart [getPos]]] "" "" inc]} {
  142.     return [file::tryAndOpen $inc]
  143.     }
  144.     
  145.     select $from $to
  146.     set text [getSelect]
  147.     
  148.     global tagFile
  149.     set lines [grep "^$text'" $tagFile]
  150.     if {[regexp {'(.*)'(.*[^\t])(\t)+∞} $lines "" one two]} {
  151.     file::openQuietly $one
  152.     set inds [search -s -f 1 -r 0 "$two" [minPos]]
  153.     display [lindex $inds 0]
  154.     eval select $inds
  155.     } else {
  156.     app::launchFore DanR
  157.     AEBuild {'DanR'} DanR {REF } "----" "“$text”"
  158.     }
  159. }
  160.  
  161. proc C++::parseFuncs {} {
  162.     global mode sortFuncsMenu funcExpr parseExpr
  163.     
  164.     set pos [minPos]
  165.     set m {}
  166.     while {[set res [search -s -f 1 -r 1 -i 0 -n $funcExpr $pos]] != ""} {
  167.     if {[regexp -- $parseExpr [eval getText $res] "" word]} {
  168.         lappend m [list $word [lindex $res 0]]
  169.     }
  170.     set pos [lindex $res 1]
  171.     }
  172.     if {$sortFuncsMenu} {
  173.     regsub -all "\[\{\}\]" [lsort -ignore $m] "" m
  174.     } else {
  175.     regsub -all "\[\{\}\]" $m "" m
  176.     }    
  177.     set files ""
  178.     foreach f [getIncludeFiles] {
  179.     lappend files $f -1
  180.     }
  181.     return [concat $files [list "(-" 0] $m]
  182. }
  183.  
  184. # for C mode
  185.  
  186. proc C::DblClick {args} { eval C++::DblClick $args }
  187.  
  188. proc C::parseFuncs {} {
  189.     return [C++::parseFuncs]
  190. }
  191.  
  192.  
  193. #############################################################################
  194. #                                                                            #
  195. # Stuff above this point has only minor modifications from the original        #
  196. # "cMode.tcl", stuff below is largely or totally new.                #
  197. #                                                                            #
  198. #############################################################################
  199.  
  200. # ◊◊◊◊ File marking ◊◊◊◊ #
  201.  
  202. ## 
  203.  # -------------------------------------------------------------------------
  204.  #     
  205.  # "C++::MarkFile" --
  206.  #    
  207.  #    Improved version which handles templates, operators    etc.
  208.  #    Makes use of the new mark menu in Alpha    6.5 which can handle
  209.  #    more weird characters.  Handles most 'operator =+-*...' functions
  210.  #  for C++
  211.  #  
  212.  #  Better marking of templates recently added.
  213.  # -------------------------------------------------------------------------
  214.  ##
  215. proc C++::MarkFile {} {
  216.     if {[file extension [win::Current]] == ".exp"} { return }
  217.     set pos [minPos]
  218.     set markExpr "^(\[A-Za-z0-9~_\]+\[ \t\n\r\]*\\(|(\[^ \t\(#\n\r/@\*\].*\[ \t\]+\\*?)?(\[A-Za-z0-9~_\]+(<\[^>\]*>)?(::)?\[-A-Za-z0-9~_+= <>\|\\*/\]+|\[A-Za-z0-9~_\]+)\[ \n\t\r\]*\\()\[^(\]"
  219.     while {![catch {search -s -f 1 -r 1 -m 0 -i 0 "$markExpr" $pos} res]} {
  220.     if {[catch {search -s -f 0 -r 1 -m 0 -l [lindex $res 0] -i 0 \
  221.       {[ *][a-zA-Z]} [set pos [pos::math [lindex $res 1] + 1]]} start]} {
  222.         set start [lindex $res 0]
  223.         if {[regexp "^\[A-Za-z0-9~_\]+\[ \t\r\n\]*\\(" \
  224.           [getText $start [nextLineStart $start]] thistext]} {
  225.         # nothing
  226.         } else {
  227.         continue
  228.         }
  229.         
  230.     } else {
  231.         set start [lindex $start 0]
  232.         set thistext [getText $start $pos]
  233.     }
  234.     #regexp doesn't like carriage returns or tabs
  235.     #if the open paren was the last character on the line the selected text 
  236.     #included the last carriage return as well
  237.     #trim this off now that it is changed into a space
  238.     regsub -all "\[\r\n\t\]" [string trimright $thistext] " " thistext
  239.     if {[regexp "(\[A-Za-z0-9~_\]+(<\[^>\]*>)?(::)?\[-A-Za-z0-9~_+= <>\|\*/\]+|\[A-Za-z0-9~_\]+)\[ \t\]*\\(" $thistext dummy word]} {
  240.         if { [string first "::" $word] != -1 } {
  241.         regsub {(<\w+>)?::} $word " " it
  242.         set l [lindex $it 0]
  243.         if { $l == [lindex $it 1] } {
  244.             set word "Construct '$l'"
  245.         } elseif { "~$l" == [lindex $it 1] } {
  246.             set word "Destruct '$l'"
  247.         }
  248.         }
  249.         set inds($word) [lineStart [pos::math $start - 1]]
  250.     }
  251.     }
  252.     if {[info exists inds]} {
  253.     foreach f [lsort -ignore [array names inds]] {
  254.         set next [nextLineStart $inds($f)]
  255.         set it $f
  256.         if {[string length $it] > 57} { set it "[string range $it 0 53]..." }
  257.         setNamedMark "${it}" "$inds($f)" $next $next
  258.     }
  259.     }
  260. }
  261.  
  262. proc C::MarkFile {} { C++::MarkFile }
  263.  
  264.  
  265. # ◊◊◊◊ Indentation routines ◊◊◊◊ #
  266.  
  267. proc C::indentLine {} {C++::indentLine}
  268.  
  269.  ## 
  270.   # -------------------------------------------------------------------------
  271.   #     
  272.   #    "C++indentLine" --
  273.   #    
  274.   #     More sophisticated    version    of Pete's.    Handles    things like    '(...)'
  275.   #     expressions split over    multiple lines,    if/elseif/else both    with and
  276.   #     without curly braces, multiple    line stream    manipulation with '<<'
  277.   #     or    '>>', C and C++ style comments, ...  Assumes indentation is '4'
  278.   #   but any tab-size may be used.
  279.   #     
  280.   #     Current bugs: multi-line ',' separated lists are poorly indented.
  281.   #
  282.   #  Problems:
  283.   #   matchIt's limit doesn't seem to work, so if there is no match and we're
  284.   #   in a large file, we wait up to seconds sometimes.  Alpha bug.
  285.   #           
  286.   #     Currently checking whether we're in a /*...*/ comment is quite
  287.   #   time consuming.  It would be nice if Alpha supplied a hook to do
  288.   #   this for us.
  289.   #   
  290.   #    Results:
  291.   #     Indents the current line correctly    ;-)    for    C, C++ coding
  292.   #     
  293.   # --Version--Author------------------Changes-------------------------------  
  294.   #      1.0      Pete Keleher              original
  295.   #    2.0     <vince@santafe.edu> updated as described above.
  296.   #    2.1     <vince@santafe.edu> faster, better, uses positions not strings
  297.   #    2.2     <vince@santafe.edu> uses 'correctIndentation' sub proc
  298.   # -------------------------------------------------------------------------
  299.   ##
  300. proc C++::indentLine {} {
  301.     global gotoEol
  302.     set gotoEol 0
  303.     
  304.     # preliminaries
  305.     set beg [lineStart [getPos]]
  306.     # are we in a C comment /*...*/ if so indent specially and return
  307.     # we really need to work out how to put this in 'correctIndentation'
  308.     if {![catch {C_inCComment $beg} comment]} {
  309.     set fChar [search -s -f 1 -r 1 "\[^ \t\r\n\]" $beg]
  310.     if { [lookAt [lindex $fChar 0]] == "*" } {
  311.         return [eval C_indentCommentLine [list $beg] $comment]
  312.     }
  313.     }
  314.     set text [getText $beg [nextLineStart $beg]]
  315.     if {[regexp "^\[ \t\]*" $text white]} {
  316.     set len [string length $white]
  317.     set rest [string range $text $len end]
  318.     } else {
  319.     set white ""
  320.     set rest ""
  321.     set len 0
  322.     }
  323.     # get indentation level    
  324.     set lwhite [text::indentOf [C++::correctIndentation [getPos] [string trim $rest]]]
  325.     if {$white != $lwhite} {
  326.     replaceText $beg [pos::math $beg + $len] $lwhite
  327.     }
  328.     if {$gotoEol} {
  329.     goto [pos::math [nextLineStart $beg] - 1]
  330.     } else {
  331.     goto [pos::math $beg + [string length $lwhite]] 
  332.     }
  333.     
  334. }
  335. proc C::correctIndentation {args} {eval C++::correctIndentation $args}
  336. ## 
  337.  # -------------------------------------------------------------------------
  338.  # 
  339.  # "C++::correctIndentation" --
  340.  # 
  341.  #  Known bugs:
  342.  #  
  343.  #  Lines which contain a URL with :// embedded tend to be considered
  344.  #  a ':' followed by a comment, and are indented as if they were
  345.  #  part of a 'case://comment' statement which is wrong.
  346.  # -------------------------------------------------------------------------
  347.  ##
  348. proc C++::correctIndentation {pos {nextword ""}} {
  349.     global gotoEol
  350.     # preliminaries
  351.     set beg [lineStart $pos]
  352.     set nextCh [string range $nextword 0 3]
  353.     set nextC [string index $nextCh 0]
  354.     set nextP [string range $nextCh 0 1]
  355.     # check for forced indentation of C, C++ comments and '#' macros
  356.     set ind "code 0"
  357.     switch -- $nextC {
  358.     "\#" {
  359.         global indentMacros
  360.         set ignore_trailers ""
  361.         set ind $indentMacros
  362.     }
  363.     "/" {
  364.         global indentComments indentC++Comments
  365.         set ignore_trailers ""
  366.         if {$nextP == "/*"} {set ind $indentComments}
  367.         if {$nextP == "//"} {set ind ${indentC++Comments}}
  368.     }
  369.     }
  370.     if {[lindex $ind 0] == "fixed" } {
  371.     # force indentation to given level
  372.     return [lindex $ind 1]
  373.     }
  374.     
  375.     # (1) first we get the indent of the last line:
  376.     # this may involve looking back a fair way
  377.     set lst [C_prevCodeIndent [pos::math $beg - 1]]
  378.     
  379.     if {[pos::compare [set pstart [lindex $lst 0]] == [minPos]]} {
  380.     return 0
  381.     }
  382.     set lwhite [posX [pos::math [lindex $lst 1] - 1]]    
  383.     # have we just finished an if-elseif-else with no '{}'?
  384.     if {$nextCh == "else"} {set iselse 1} else { set iselse 0}
  385.     if {![C_isLineNBI $pstart]} {
  386.     incr lwhite [C_recurseNoBraceIndent $pstart 0 $iselse]
  387.     }
  388.     if { [set multi [C_isLineMulti $pstart]] != "-1" } {
  389.     set lwhite $multi
  390.     }
  391.     
  392.     # (2) now we indent this line accordingly
  393.     
  394.     set pbeg [prevLineStart $beg]
  395.     set backpos [nextLineStart [lindex $lst 0]]
  396.     # is there a comment at the end of the line? if so scan back to the character we want
  397.     if {![catch {search -s -f 0 -r 1 -l $pbeg "//\[^\r\n\]*\[\n\r\]" $backpos} compos]} {
  398.     set compos [lindex $compos 0]
  399.     if {[pos::compare $compos > $pbeg]} {
  400.         set backpos [pos::math $compos + 1]
  401.     }    
  402.     }
  403.     global indentationAmount
  404.     if {[pos::compare [set backpos [pos::math $backpos - 2]] > [minPos]]} {
  405.     set lst [search -s -f 0 -r 1 -m 0 "\[^ \t\r\n\]" $backpos]
  406.     switch -- [lookAt [lindex $lst 0]] {
  407.         "\{" {
  408.         incr lwhite $indentationAmount
  409.         } 
  410.         ":" {
  411.         # expression is better for odd indentationAmounts
  412.         incr lwhite [expr {$indentationAmount - $indentationAmount/2}]
  413.         } 
  414.         "\)" {
  415.         # see if we're in a if-elseif-else with no '{}' and indent
  416.         if {[C_isLineNBI $pstart]} {
  417.             incr lwhite $indentationAmount
  418.         }
  419.         }
  420.         "e" {
  421.         if { [getText [pos::math [lindex $lst 0] - 3] \
  422.           [pos::math [lindex $lst 0] + 1]] == "else" } {
  423.             if {[C_isLineNBI $pstart]} {
  424.             incr lwhite $indentationAmount
  425.             }
  426.         }
  427.         
  428.         }
  429.     }
  430.     }
  431.     
  432.     switch -- $nextC {
  433.     "\}" {
  434.         incr lwhite [expr -$indentationAmount]
  435.     }
  436.     "<" -
  437.     ">" {            
  438.         # indent for '<<' and '>>' in multi-line C++ stream manipulation
  439.         if {$nextP == "<<" || $nextP == ">>"} {
  440.         set strm [search -s -f 1 -r 1 "^\[^${nextC}\]+${nextP}" $pbeg]
  441.         set lwhite [posX [pos::math [lindex $strm 1] - 2]]
  442.         set gotoEol 1
  443.         }
  444.     }
  445.     }
  446.     # Check if we're in a multi-line '(.....)' if so align to start
  447.     global useFasterButWorseIndentation
  448.     if {!$useFasterButWorseIndentation && ![catch {matchIt ")" $beg 200} paren]} {
  449.     set lwhite [posX [pos::math $paren + 1]]
  450.     set gotoEol 1
  451.     }
  452.  
  453.     if {[regexp "^(case\[ \t\].*|\[a-zA-Z\]+):(\[^:\]|\$)" $nextword] && $lwhite > 3 \
  454.       && ![info exists ignore_trailers]} {
  455.     incr lwhite [expr -$indentationAmount/2]
  456.     }
  457.     # get indentation level    
  458.     return [incr lwhite [lindex $ind 1]]
  459. }
  460.  
  461. ## 
  462.  # -------------------------------------------------------------------------
  463.  # 
  464.  # "C++::electricLeft" --
  465.  # 
  466.  #  For those who like to place left braces after 'for', 'else' etc. on 
  467.  #  their own line, this will ensure the left brace is correctly placed.
  468.  # -------------------------------------------------------------------------
  469.  ##
  470. proc C++::electricLeft {} {
  471.     if {[string trim [getText [lineStart [getPos]] [getPos]]] == ""} {
  472.     if {[C_isLineNBI [prevLineStart [getPos]]]} {
  473.         set p [lineStart [getPos]]
  474.         insertText "\{\r"
  475.         goto $p
  476.         shiftLeft
  477.         goto [nextLineStart $p]
  478.         bind::IndentLine
  479.         return
  480.     }
  481.     }
  482.     ::electricLeft
  483. }
  484.  
  485. proc C::electricLeft {} {C++::electricLeft}
  486.  
  487. ## 
  488.  # -------------------------------------------------------------------------
  489.  #     
  490.  # "recurseNoBraceIndent" --
  491.  #    
  492.  # Scans back until we no longer have a 'no brace indent'.  A 'no brace
  493.  # indent' is a 'for', 'if' etc which didn't use '{ ...  }'
  494.  # -------------------------------------------------------------------------
  495.  ##
  496. proc C_recurseNoBraceIndent {pos offset {iselse 0}} {
  497.     set pos [prevLineStart $pos]
  498.     if {[C_isLineNBI $pos]} {
  499.     global indentationAmount
  500.     if {$iselse} {
  501.         set p [text::firstNonWsPos $pos]
  502.         set t [getText $p [pos::math $p + 10]]
  503.         if {[regexp  "(else\[ \t\]+)?if.*" $t]} {
  504.         return [expr {$offset -$indentationAmount}]
  505.         }
  506.     }
  507.     return [C_recurseNoBraceIndent $pos [incr offset [expr -$indentationAmount]] $iselse]
  508.     }
  509.     return $offset
  510.     
  511. }
  512.  
  513. ## 
  514.  # -------------------------------------------------------------------------
  515.  #     
  516.  # "isLineNBI" --
  517.  #    
  518.  #  Tests if the given line is a 'no brace indent'.  'pos' is the beginning
  519.  #  of the line in question, else this proc will fail.
  520.  # -------------------------------------------------------------------------
  521.  ##
  522. set C_recNBI "^\[ \t\]*((\}?\[ \t\]*(if|else\[ \t\]+if)|for)\[ \t\]*\\(.*\\)|\}?\[ \t\]*else)\[ \t\]*(//\[^\r\n\]*)?\[ \t\r\n\]*"
  523. proc C_isLineNBI {pos} {
  524.     global C_recNBI
  525.     if {![catch {search -s -f 1 -r 1 -l [nextLineStart [nextLineStart $pos]] $C_recNBI $pos} ifelse] } {
  526.     if {[pos::compare $pos == [lindex $ifelse 0]]} {
  527.         if {[pos::compare [lindex $ifelse 1] == [maxPos]] || ([lookAt [lindex $ifelse 1]] != "\{")} {
  528.         return 1 
  529.         }
  530.     }
  531.     }
  532.     return 0
  533. }
  534.  
  535. # use 'catch' to call this proc: error = no, otherwise returns st,end pos
  536. proc C_inCComment {pos} {
  537.     set cS [search -s -f 0 -r 0 -l [pos::math $pos - 1000] "/*" $pos]
  538.     set cE [search -s -f 1 -r 0 -l [pos::math $pos + 1000] "*/" [lindex $cS 1]]
  539.     if {[pos::compare $pos >= [lindex $cE 1]] } {
  540.     error "No"
  541.     } else {
  542.     return [list [lindex $cS 0] [lindex $cE 1]]
  543.     }
  544. }
  545.  
  546. # look for '<<' and '(...)' multi lines.
  547. proc C_isLineMulti {pos} {
  548.     # look for multi-line '(...)'
  549.     if { ![catch {search -s -f 0 -r 1 -l $pos {\).*$} [nextLineStart $pos]} paren] \
  550.       && [pos::compare [nextLineStart $pos] == [pos::math [lindex $paren 1] + 1 ]] } {
  551.     if {[catch {matchIt "\)" [pos::math [lindex $paren 0] - 1] 200} realStart]} {
  552.         return -1
  553.     }
  554.     if {[pos::compare [lineStart $realStart] != [lineStart [lindex $paren 0]]] } {
  555.         set lst [search -s -f 0 -r 1 -i 0 "^\[ \t\]*\[^ \t\r\n\]" $realStart]
  556.         return [posX [pos::math [lindex $lst 1] - 1]]
  557.     }
  558.     }
  559.     # look for multi-line '<<' or '>>'
  560.     set p $pos
  561.     while {![catch {search -s -f 1 -r 1 -l [nextLineStart $p] "^\[ \t\]*(<<|>>)" $p} strm] } {
  562.     set p [prevLineStart $p]
  563.     }
  564.     if { $p != $pos } {
  565.     set lst [search -s -f 1 -r 1 -i 0 "^\[ \t\]*\[^ \t\r\n\]" $p]
  566.     return [posX [pos::math [lindex $lst 1] - 1]]
  567.     }
  568.     
  569.     return -1
  570.     
  571. }
  572.  
  573. ## 
  574.  # -------------------------------------------------------------------------
  575.  #   
  576.  # "C_indentCommentLine" --
  577.  #  
  578.  #  Indents a line within a multi-line /* ... */ comment correctly.
  579.  # -------------------------------------------------------------------------
  580.  ##
  581. proc C_indentCommentLine {beg cS cE} {
  582.     set lwhite [text::minSpaceForm [getText [lineStart $cS] $cS]]
  583.     if {[pos::compare $beg != [lineStart [lindex $cE 0]]] \
  584.       || [text::firstNonWs [pos::math $beg - 1]] == "*" } {
  585.     append lwhite " "    
  586.     }
  587.     
  588.     set text [getText $beg [nextLineStart $beg]]
  589.     regexp "^\[ \t\]*" $text white
  590.     set len [string length $white]
  591.     if {$white != $lwhite} {
  592.     replaceText $beg [pos::math $beg + $len] $lwhite
  593.     }
  594.     goto [pos::math $beg + [expr {[string length $lwhite] +1}]]
  595. }
  596.  
  597.  
  598. ## 
  599.  # -------------------------------------------------------------------------
  600.  #   
  601.  # "C_prevCodeIndent" --
  602.  #  
  603.  #  Find the indent of the previous line
  604.  #  -  If it's the start of the file, return 0 0 (special case)
  605.  #  else
  606.  #  -  if it's a C++ comment, keep looking backwards (so you can offset
  607.  #     C++ comments if you so desire)
  608.  #  -  if it's a C comment, get the indentation of the '/*' not some
  609.  #     intermediate point.
  610.  # -------------------------------------------------------------------------
  611.  ##
  612. proc C_prevCodeIndent {pos} { 
  613.     while {1} {
  614.     if {[pos::compare $pos == [minPos]] \
  615.       || ([catch {search -s -m 0 -f 0 -r 1 -i 0 "^\[ \t\]*\[^ \t\r\n\]" $pos} p]) \
  616.       || $p == "[minPos] [minPos]" } {
  617.         return [list [minPos] [pos::math [minPos] + 1]]
  618.     } else {
  619.         set pp [doubleLookAt [pos::math [lindex $p 1] - 1]]
  620.         if { $pp == "//" } {
  621.         set pos [pos::math [lindex $p 0] - 1]
  622.         } elseif { [string index $pp 0] == "#" } {
  623.         global indentMacros
  624.         if {$indentMacros == "code 0"} {
  625.             break
  626.         }
  627.         set pos [pos::math [lindex $p 0] - 1]
  628.         } elseif { $pp == "*/" } {
  629.         set pos [lindex [search -s -f 0 -r 0 "/*" \
  630.           [pos::math [lindex $p 0] - 1]] 0]
  631.         } elseif { ![catch {set comment [C_inCComment [lindex $p 0]]} ] } {
  632.         set pos [pos::math [lineStart [lindex $comment 0]] - 1]
  633.         #return [text::indentation [lindex $comment 0]] (old style)
  634.         } else {
  635.         break
  636.         }
  637.     }
  638.     }
  639.     return $p
  640. }
  641.  
  642.  
  643. # ◊◊◊◊ Electric routines ◊◊◊◊ #
  644.  
  645. proc C::carriageReturn {} {C++::carriageReturn}
  646. proc C::OptionTitlebar {} {C++::OptionTitlebar}
  647. proc C::OptionTitlebarSelect {item} {C++::OptionTitlebarSelect $item}
  648.  
  649. ## 
  650.  # -------------------------------------------------------------------------
  651.  #     
  652.  # "C++::carriageReturn" --
  653.  #    
  654.  #    Called by the general routine 'carriageReturn'.    We know    no selection 
  655.  #    exists, and we are not inside a block comment.
  656.  # -------------------------------------------------------------------------
  657.  ##
  658. proc C++::carriageReturn {} {
  659.     if {[lookAt [pos::math [getPos] - 1]] == ":"} {
  660.     if {[regexp {[\r\n]} [lookAt [getPos]]]} {
  661.         bind::IndentLine
  662.         endOfLine
  663.         insertText "\r"
  664.     } else {
  665.         set pos [getPos]
  666.         endOfLine
  667.         set t [getText $pos [getPos]]
  668.         replaceText $pos [getPos] ""
  669.         bind::IndentLine
  670.         endOfLine
  671.         insertText "\r"
  672.         insertText $t
  673.     }
  674.     } else {
  675.     insertText "\r"
  676.     }
  677.     catch {bind::IndentLine}
  678. }
  679.  
  680. proc C++::OptionTitlebar {} {
  681.     if {![catch {C++::tryIDEget} ret] && ![regexp {\(No} $ret] } { return $ret }
  682.     # else just scan through, provided the scan will function
  683.     set cid [scancontext create]
  684.     set lines {}
  685.     scanmatch $cid {#.*include.*(<|")(.*)(>|")}  {lappend lines $matchInfo(submatch1)}
  686.     set fid [open [stripNameCount [win::Current]] "r"]
  687.     scanfile $cid $fid
  688.     close $fid
  689.     scancontext delete $cid
  690.     return [lsort -ignore $lines]
  691. }
  692.  
  693. proc C++::OptionTitlebarSelect {fname} {
  694.     C++::tryIDEedit $fname
  695. }
  696.  
  697. proc C++::tryIDEget {} {
  698.     global IDE
  699.     switch -- $IDE {
  700.     1 {thinkGetIncludeFiles}
  701.     0 {cw::getIncludeFiles}
  702.     2 {error "No IDE at all!"}
  703.     }
  704. }
  705.  
  706. proc C++::tryIDEedit {fname} {
  707.     global IDE
  708.     switch -- $IDE {
  709.     1 {thinkEditIncludeFile $fname}
  710.     0 {cw::editIncludeFile $fname}
  711.     2 {error "No IDE at all!"}
  712.     }
  713. }
  714.  
  715.